﻿using System;
using System.IO;
using ParserGenerator.Exceptions;
using ParserGenerator.GrammarAnalysis;
using ParserGenerator.ParserConstruction;
using ParserGenerator.ScannerConstruction;
using ParserGenerator.SyntacticAnalysis;

namespace ParserGenerator.GrammarConstruction
{
    [Serializable]
    public sealed class GrammarDescription
    {
        public GrammarDeclaration Declaration { get; }
        public ScannerAutomatonStartTable Starts { get; }
        public GrammarTerminalTable Tokens { get; }
        public GrammarGoalDescriptorTable Goals { get; }
        public LexicalPrecedenceTable LexicalPrecedences { get; }
        public GrammarNonterminalTable Nonterminals { get; }
        public GrammarProductionTable Productions { get; }
        public ExceptionDescriptorTable Exceptions { get; }
        public ScannerAutomaton ScannerAutomaton { get; }
        public ParserAutomaton ParserAutomaton { get; }

        public GrammarDescription(GrammarDeclaration declaration)
        {
            if (declaration == null)
                throw new ArgumentNullException(nameof(declaration));
            Declaration = declaration;
            GrammarAnalyzer analyzer = new GrammarAnalyzer(declaration);
            Starts = analyzer.Starts;
            Tokens = analyzer.Tokens;
            Goals = analyzer.Goals;
            LexicalPrecedences = analyzer.LexicalPrecedences;
            Nonterminals = analyzer.Nonterminals;
            Productions = analyzer.Productions;
            Exceptions = analyzer.Exceptions;
            ScannerAutomaton = analyzer.ScannerAutomaton;
            ParserAutomaton = analyzer.ParserAutomaton;
        }

        public static GrammarDescription FromFile(string path)
        {
            if (path == null)
                throw new ArgumentNullException(nameof(path));
            return FromFile(path, true);
        }

        public static GrammarDescription FromFile(string path, bool recursive)
        {
            if (path == null)
                throw new ArgumentNullException(nameof(path));
            GrammarDeclaration g = GrammarDeclaration.LoadFromFile(path, recursive);
            return new GrammarDescription(g);
        }

        private string GetDefaultLoggingPath()
        {
            if (Declaration.Context == null)
                return null;
            string path = Declaration.Context.Document.FullName;
            if (path.IndexOfAny(Path.GetInvalidPathChars()) != -1)
                return null;
            return path + ".log";
        }

        public void Dump()
        {
            string path = GetDefaultLoggingPath();
            if (path == null)
            {
                GrammarErrorException e = new GrammarErrorException(SR.FailToGetGrammarLoggingFilePath);
                e.AddLocation(Declaration.Context);
                throw e;
            }
            DumpToDirectory(path);
        }

        public void DumpToDirectory(string path)
        {
            if (path == null)
                throw new ArgumentNullException(nameof(path));
            GrammarDescriptionDumper dumper = new GrammarDescriptionDumper();
            dumper.Dump(this, path);
        }
    }
}
